
#if !defined VPROCESSOR
#define VPROCESSOR

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

// Interface do processador de frames
class FrameProcessor {

  public:
    // Metodo de processamento
	virtual void process(cv:: Mat &input, cv:: Mat &output)= 0;
};

class VideoProcessor {

  private:

      // Objeto
	  cv::VideoCapture capture;

      // Funcao callback para processar cada frame
	  void (*process)(cv::Mat&, cv::Mat&);

      // Ponteiro para a classe que implementa a interface
	  FrameProcessor *frameProcessor;

      // Indica se o processo callback sera chamado
	  bool callIt;

	  std::string windowNameInput;

	  std::string windowNameOutput;

      // delay  entre cada frame processado
	  int delay;

      // numero de frames procesados
      long fnumber;

      // indica frame de parada
      long frameToStop;

      // indica fim do processamento
	  bool stop;

      std::vector<std::string> images;

      std::vector<std::string>::const_iterator itImg;

	  cv::VideoWriter writer;

	  std::string outputFile;

	  int currentIndex;

      int digits;

	  std::string extension;

      // pega o proximo frame
      bool readNextFrame(cv::Mat& frame) {

          if (images.size()==0)
              return capture.read(frame);
          else {

              if (itImg != images.end()) {

                  frame= cv::imread(*itImg);
                  itImg++;
                  return frame.data != 0;
              }
          }
      }


	  void writeNextFrame(cv::Mat& frame) {

		  if (extension.length()) { // then we write images
		  
			  std::stringstream ss;
		      ss << outputFile << std::setfill('0') << std::setw(digits) << currentIndex++ << extension;
			  cv::imwrite(ss.str(),frame);

		  } else { // then write video file

			  writer.write(frame);
		  }
	  }

  public:

	  VideoProcessor() : callIt(false), delay(-1), 
		  fnumber(0), stop(false), digits(0), frameToStop(-1), 
	      process(0), frameProcessor(0) {}

      // Nomeia o arquivo de video
	  bool setInput(std::string filename) {

		fnumber= 0;
		capture.release();
        images.clear();

        // Abre o video
		return capture.open(filename);
	  }


      void setFrameProcessor(void (*frameProcessingCallback)(cv::Mat&, cv::Mat&)) {

          // invalidate frame processor class instance
          frameProcessor= 0;
          // this is the frame processor function that will be called
          process= frameProcessingCallback;
          callProcess();
      }

	  void callProcess() {

		  callIt= true;
	  }

	  void dontCallProcess() {

		  callIt= false;
	  }

	  void displayInput(std::string wn) {
	    
		  windowNameInput= wn;
		  cv::namedWindow(windowNameInput);
	  }

	  void displayOutput(std::string wn) {
	    
		  windowNameOutput= wn;
		  cv::namedWindow(windowNameOutput);
	  }


	  void setDelay(int d) {
	  
		  delay= d;
	  }

	  cv::Size getFrameSize() {

		if (images.size()==0) {

			int w= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH));
			int h= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT));

			return cv::Size(w,h);

        } else {

			cv::Mat tmp= cv::imread(images[0]);
			if (!tmp.data) return cv::Size(0,0);
			else return tmp.size();
		}
	  }


      double getFrameRate() {

          if (images.size()!=0) return 0;

          double r= capture.get(CV_CAP_PROP_FPS);
          return r;
      }

	  void stopIt() {

		  stop= true;
	  }

	  bool isStopped() {

		  return stop;
	  }

	  bool isOpened() {

		  return capture.isOpened() || !images.empty();
	  }
	  

	  void run() {

		  cv::Mat frame;

		  cv::Mat output;

		  if (!isOpened())
			  return;

		  stop= false;

		  while (!isStopped()) {

			  if (!readNextFrame(frame))
				  break;

			  if (windowNameInput.length()!=0) 
				  cv::imshow(windowNameInput,frame);

			  if (callIt) {
				  
				if (process)
				    process(frame, output);
				else if (frameProcessor) 
					frameProcessor->process(frame,output);
			    fnumber++;

			  } else {

				output= frame;
			  }

			  if (outputFile.length()!=0)
				  writeNextFrame(output);

			  if (windowNameOutput.length()!=0) 
				  cv::imshow(windowNameOutput,output);
			
			  if (delay>=0 && cv::waitKey(delay)>=0)
				stopIt();

		  }
	  }
};

#endif
